package com.morningstar.system.service;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.morningstar.common.constants.SysConfigConstants;
import com.morningstar.common.dao.entity.CommonQuery;
import com.morningstar.common.result.Result;
import com.morningstar.common.result.ServiceCheckResult;
import com.morningstar.system.controller.entity.UpdatePassword;
import com.morningstar.system.controller.query.SysUserQuery;
import com.morningstar.system.controller.result.ArcoDesignFile;
import com.morningstar.system.dao.entity.*;
import com.morningstar.system.dao.mapper.SysUserMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务类
 * </p>
 *
 * @author yls
 * @since 2023-06-15
 */
@Slf4j
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private SysDeptService sysDeptService;

    @Resource
    private SysMenuService sysMenuService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private SysUserPostService sysUserPostService;

    @Resource
    private SysFileService sysFileService;

    /**
     * 前端分页查询
     *
     * @param commonQuery 通用查询
     * @return 用户分页
     */
    public IPage<SysUser> selectPage(CommonQuery<SysUser, SysUserQuery> commonQuery) {
        if (commonQuery.getQuery() == null) {
            commonQuery.setQuery(new SysUserQuery());
        }
        return sysUserMapper.selectPage(commonQuery.getPage(), commonQuery.getQuery());
    }

    @Transactional(rollbackFor = Exception.class)
    public void add(SysUser sysUser) {
        String initPassword = redisTemplate.opsForValue().get(SysConfigConstants.PASSWORD_INIT_KEY);
        if (StringUtils.isBlank(initPassword)) {
            initPassword = sysUser.getLoginName() + "@123";
        }
        sysUser.setPassword(BCrypt.hashpw(initPassword));
        sysUserMapper.insert(sysUser);
        sysUserRoleService.addUserRole(sysUser);
        sysUserPostService.addUserPost(sysUser);
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(SysUser sysUser) {
        sysUserMapper.updateById(sysUser);
        sysUserRoleService.addUserRole(sysUser);
        sysUserPostService.addUserPost(sysUser);
    }

    public SysUser detail(Long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper<SysUserRole>()
                .eq(SysUserRole::getUserId, userId);
        List<Long> roleIds = sysUserRoleService.list(wrapper)
                .stream()
                .map(SysUserRole::getRoleId)
                .toList();
        sysUser.setRoleIds(roleIds);
        LambdaQueryWrapper<SysUserPost> postWrapper = new LambdaQueryWrapper<SysUserPost>()
                .eq(SysUserPost::getUserId, userId);
        List<Long> postIds = sysUserPostService.list(postWrapper)
                .stream()
                .map(SysUserPost::getPostId)
                .toList();
        sysUser.setPostIds(postIds);
        return sysUser;
    }

    /**
     * 登录用户信息
     *
     * @return 用户信息
     */
    public SysUser loginUserDetail() {
        String loginId = StpUtil.getLoginId().toString();
        SysUser sysUser = this.getById(loginId);
        sysUser.setPassword(null);
        SysDept sysDept = sysDeptService.getById(sysUser.getDeptId());
        if (sysDept != null) {
            sysUser.setDeptName(sysDept.getDeptName());
        }
        List<String> permissions = sysMenuService.selectMenuPerms(Long.parseLong(loginId));
        sysUser.setPermissions(permissions);
        sysUser.setRoleNames(sysUserMapper.selectUserRoles(sysUser.getUserId()));
        sysUser.setPostNames(sysUserMapper.selectUserPosts(sysUser.getUserId()));
        SysFile sysFile = sysFileService.getById(sysUser.getAvatar());
        if (sysFile != null) {
            ArcoDesignFile arcoDesignFile = ArcoDesignFile.success(sysFile, sysFileService.getUrlPrefix());
            List<ArcoDesignFile> arcoDesignFiles = new ArrayList<>();
            arcoDesignFiles.add(arcoDesignFile);
            sysUser.setArcoDesignFiles(arcoDesignFiles);
        }
        return sysUser;
    }

    public Result<Object> updatePassword(UpdatePassword updatePassword) {
        String loginId = StpUtil.getLoginId().toString();
        SysUser sysUser = sysUserMapper.selectById(loginId);
        if (!BCrypt.checkpw(updatePassword.getOldPassword(), sysUser.getPassword())) {
            return Result.fail("旧密码错误");
        }
        if (!StringUtils.equals(updatePassword.getNewPassword(), updatePassword.getConfirmPassword())) {
            return Result.fail("二次确认密码错误");
        }
        sysUser.setPassword(BCrypt.hashpw(updatePassword.getNewPassword()));
        sysUser.setPwdUpdateDate(LocalDateTime.now());
        sysUserMapper.updateById(sysUser);
        return Result.successNoData();
    }

    public Result<Object> resetPassword(Long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        String initPassword = redisTemplate.opsForValue().get(SysConfigConstants.PASSWORD_INIT_KEY);
        if (StringUtils.isBlank(initPassword)) {
            initPassword = sysUser.getLoginName() + "@123";
        }
        sysUser.setPassword(BCrypt.hashpw(initPassword));
        sysUserMapper.updateById(sysUser);
        return Result.successNoData();
    }

    public Map<Long, String> selectUserIdAndName() {
        return sysUserMapper.selectAll().stream().collect(Collectors.toMap(SysUser::getUserId, SysUser::getUserName));
    }

    public void userLoginSuccess(Long loginId, String ip) {
        LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<SysUser>()
                .set(SysUser::getLoginDate, LocalDateTime.now())
                .set(SysUser::getLoginIp, ip)
                .eq(SysUser::getUserId, loginId);
        this.update(wrapper);
    }

    /**
     * 新增 修改时校验用户信息
     *
     * @param sysUser 用户信息
     * @return 校验结果
     */
    public ServiceCheckResult checkAddAndUpdate(SysUser sysUser) {
        // 校验登录名重复
        LambdaQueryWrapper<SysUser> checkLoginNameWrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getLoginName, sysUser.getLoginName())
                .ne(sysUser.getUserId() != null, SysUser::getUserId, sysUser.getUserId());
        if (this.count(checkLoginNameWrapper) > 0) {
            return new ServiceCheckResult(true, "登录名已存在");
        }
        // 校验手机号重复
        LambdaQueryWrapper<SysUser> checkPhoneWrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone, sysUser.getPhone())
                .ne(sysUser.getUserId() != null, SysUser::getUserId, sysUser.getUserId());
        if (this.count(checkPhoneWrapper) > 0) {
            return new ServiceCheckResult(true, "手机号已存在");
        }
        // 校验邮箱重复
        LambdaQueryWrapper<SysUser> checkEmailWrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getEmail, sysUser.getEmail())
                .ne(sysUser.getUserId() != null, SysUser::getUserId, sysUser.getUserId());
        if (this.count(checkEmailWrapper) > 0) {
            return new ServiceCheckResult(true, "邮箱已存在");
        }
        return new ServiceCheckResult(false);
    }

    /**
     * 删除时校验用户信息
     *
     * @param userId 用户Id
     * @return 校验结果
     */
    public ServiceCheckResult checkDelete(Long userId) {
        // TODO 校验逻辑
        return new ServiceCheckResult(false);
    }
}
